﻿#include "pch.h"
#include "GameEngine.h"

#include "core/log/LogSystem.h"
#include "function/EventSystem/ApplicationEvent.h"
#include "function/RenderSystem/RenderCommand.h"

namespace Engine
{
	GameEngine* GameEngine::instance = nullptr;

	GameEngine::GameEngine()
	{
		ENGINE_ASSERT(!instance, "单例引擎已初始化！")
		instance = this;

		LogSystem::Init();
		window = Scope<AbstractWindow>(AbstractWindow::Create());
		window->SetEventCallback(BIND_EVENT_FUNCTION(GameEngine::OnEvent));
		window->SetVSync(true);

		RenderCommand::Init();

		imGuiLayer = new ImGuiLayer();
		PushOverlay(imGuiLayer);
		// todo:添加需要的engine layer

		ENGINE_TRACE("GameEngine 启动")
	}

	GameEngine::~GameEngine()
	{
		ENGINE_TRACE("GameEngine 关闭")
	}

	void GameEngine::OnEvent(Event& event)
	{
		EventDispatcher dispatcher(event);
		dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FUNCTION(GameEngine::OnWindowClose));

		for (auto iterator = layerStack.end(); iterator != layerStack.begin();)
		{
			// 反向遍历layerStack，栈的先进后出
			(*(--iterator))->OnEvent(event);

			if (event.Handled)
			{
				break;
			}
		}
	}

	void GameEngine::PushLayer(Layer* layer)
	{
		layerStack.PushLayer(layer);
		layer->OnAttach();
	}

	void GameEngine::PushOverlay(Layer* layer)
	{
		layerStack.PushOverlay(layer);
		layer->OnAttach();
	}

	bool GameEngine::TickOneFrame(const float deltaTime)
	{
		TickLogic(deltaTime);
        CalculateFPS(deltaTime);
		TickRender(deltaTime);

		window->SetWindowTitle(std::string("Mini Game Engine - " + std::to_string(fps) + " FPS").c_str());

		return !shouldClose;
	}

	float GameEngine::CalculateDeltaTime()
	{
		// 单位：毫秒
		float deltaTime;
		{
			using namespace std::chrono;

			const steady_clock::time_point time = steady_clock::now();
			const duration<float> timeSpan = duration_cast<duration<float>>(time - lastFrameTime);
			deltaTime = timeSpan.count();

			lastFrameTime = time;
		}
		return deltaTime * 1000;
	}

	void GameEngine::CalculateFPS(const float deltaTime)
	{
        fps = static_cast<int>(1.0f / (deltaTime / 1000));
	}

	void GameEngine::TickLogic(const float deltaTime)
	{
		for (Layer* layer : layerStack)
		{
			layer->OnUpdate(deltaTime);
		}

		window->OnUpdate();
	}

	void GameEngine::TickRender(const float deltaTime)
	{
		imGuiLayer->BeforeImGuiRender();
		for (Layer* layer : layerStack)
		{
			layer->OnImGuiRender();
		}
		imGuiLayer->AfterImGuiRender();
	}

	bool GameEngine::OnWindowClose(WindowCloseEvent& event)
	{
		shouldClose = true;
		return true;
	}
}
